﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DTGroup.iNek.Configuration;
using DTGroup.iNek.Business;
using System.IO;
using System.Data;
using DTGroup.iNek.Model;
using System.Threading.Tasks;
using System.Diagnostics;
using DTGroup.iNek.Utility;
using System.Threading;
using NLog;

namespace ConsoleApplication1
{



    class Program
    {
      
        static void Main(string[] args)
        {
             Logger logger = new NLog.LogFactory().GetCurrentClassLogger();

            //string arg = @"10000&2000000&578&G:\大唐武汉\丽水\仿真工参-丽水.xlsx&G:\0&G:\0&13360270.32_3317876.51";
            // sliceLength + "&" + FileInfoCount + "&" + city + "&" + cellPath + "&" + winpropOutPut + "&" + fpdPath + "&" + fpdFilePath + "&";

            string arg = args[0];
            logger.Info("参数信息:" + arg);
            string[] pamr = arg.Split('&');

            backgroundWorker1DoWork(Convert.ToInt32(pamr[0]), Convert.ToInt32(pamr[1]), pamr[2], pamr[3], pamr[4], pamr[5], pamr[6], pamr[7]);
        }


        static void backgroundWorker1DoWork(int sliceLength, int FileInfoCount, string city, string cellPath, string winpropOutPut, string fpdPath, string fpdFilePath, string ke)
        {
            string p_day = DateTime.Now.ToString("yyyyMMddHH");

            DataTable tabCell = DTGroup.Networking.Framework.Utils.ExcelUtil.ExcelToDataTable(cellPath, "站点信息", true);

            //1、判断小区类型，过滤室内小区
            //2、根据工参里最大最小经纬度，划定范围
            //3、根据1000米划分片
            //4、确定每个分片经纬度范围
            //5、根据分片范围外扩500米，查询小区
            //6、加载分片范围内小区数据到一个集合
            //7、过滤范围外的栅格
            //8、过滤高层室外栅格
            //9、根据栅格ID分组
            //10、按RSRP排序，取前11条
            //11、最大值为主服务小区
            //12、过滤主服务小区小于-120的分组
            //12、写文件

            #region 1、判断小区类型，过滤室内小区

            List<Res_Cell> allCell = new List<Res_Cell>();

            foreach (DataRow dr in tabCell.Rows)
            {
                if (dr["基站类型"].ToString().Equals("室外"))
                {
                    Res_Cell cell = new Res_Cell();

                    double mx = 0, my = 0;

                    cell.CellName = dr["小区名称"].ToString();
                    cell.HorizonAngleToEast = (int)Convert.ToDouble(dr["扇区方位角"]);
                    cell.Longitude = Convert.ToDouble(dr["小区经度"]);
                    cell.Latitude = Convert.ToDouble(dr["小区纬度"]);
                    cell.ObjectID = Convert.ToInt32(dr["ObjectId"]);

                    loc_to_mercator(cell.Longitude, cell.Latitude, ref mx, ref my);

                    cell.CellLongitude = mx;
                    cell.CellLatitude = my;

                    allCell.Add(cell);
                }
            }

            tabCell.Dispose();
            tabCell = null;

            string indoorGridFile = System.AppDomain.CurrentDomain.BaseDirectory + "\\" + city + ".txt";
            HashSet<string> lsIndoorGrid = new HashSet<string>();

            List<Res_Cell> allCellNew = new List<Res_Cell>();

            using (StreamReader sr = new StreamReader(indoorGridFile))
            {
                string line = sr.ReadLine();

                do
                {
                    lsIndoorGrid.Add(line);

                    //double x = Convert.ToDouble(line.Split('_')[0]) * 5;
                    //double y = Convert.ToDouble(line.Split('_')[1]) * 5;

                    //foreach (var o in allCell)
                    //{
                    //    if ((!allCellNew.Contains(o)) && ((o.CellLongitude - 500) <= x && (o.CellLongitude + 500) >= x && (o.CellLatitude - 500) <= y && (o.CellLatitude + 500) >= y))
                    //    {
                    //        allCellNew.Add(o);
                    //    }
                    //}

                    line = sr.ReadLine();
                } while (!string.IsNullOrEmpty(line));
            }


            #endregion

            #region 3、根据1000米划分片 4、确定每个分片经纬度范围 5、根据分片范围外扩500米，查询小区

            Dictionary<string, List<Res_Cell>> dicLsCell = new Dictionary<string, List<Res_Cell>>();

            double sliceMinX = Convert.ToDouble(ke.Split('_')[0]);
            double sliceMinY = Convert.ToDouble(ke.Split('_')[1]);
            double sliceMaxX = sliceMinX + sliceLength;
            double sliceMaxY = sliceMinY + sliceLength;

            List<Res_Cell> lsCell = allCell.Where(o => o.CellLongitude >= sliceMinX - 500 && o.CellLongitude <= sliceMaxX + 500 && o.CellLatitude >= sliceMinY - 500 && o.CellLatitude <= sliceMaxY + 500).ToList();
            allCell.Clear();
            allCell = null;

            #endregion

            int fileInfoNum = 0;
            int fileInfoCount = FileInfoCount;// 1000000;

            FileStream fs = new System.IO.FileStream(fpdFilePath, FileMode.Append, FileAccess.Write, FileShare.Write);
            StreamWriter sw = new StreamWriter(fs);

            string[] nodeFolder = Directory.GetDirectories(winpropOutPut);

            foreach (string nodePath in nodeFolder)
            {
                List<Fpd_s_join_n_over0innerdoor> lsFPD = new List<Fpd_s_join_n_over0innerdoor>();
                int floorNum = Convert.ToInt32(GetName(nodePath));
                foreach (Res_Cell cell in lsCell)
                {
                    string filePath = nodePath + "\\" + cell.CellName + "_OutdoorOutput.txt";
                    //Console.WriteLine("filePath:" + filePath);
                    if (File.Exists(filePath))
                    {
                        #region 6、加载分片范围内小区数据到一个集合 7、过滤范围外的栅格

                        using (StreamReader sr = new StreamReader(filePath))
                        {
                            string line = sr.ReadLine();
                            do
                            {
                                string[] lins = line.Split('\t');

                                double mx = 0.0;
                                double my = 0.0;

                                if (lins.Length >= 3 && Double.TryParse(lins[0], out mx) && Double.TryParse(lins[1], out my)) 
                                {
                                    string grid = (int)mx / 5 + "_" + (int)my / 5;

                                    if (mx > sliceMinX && mx < sliceMaxX && my > sliceMinY && my < sliceMaxY)
                                    {
                                        if ((floorNum > 0 && lsIndoorGrid.Contains(grid)) || (floorNum == 0))
                                        {
                                            string rsrp = lins[2];
                                            double lon = 0, lat = 0;

                                            mercator_to_loc(mx, my, ref lon, ref lat);

                                            int calibrateGridId = ComputeCalibrateGridID(
                                                GeometryUtil.ConvertToMeracatol(new GeometryPoint(cell.Longitude, cell.Latitude)),
                                                GeometryUtil.ConvertToMeracatol(new GeometryPoint(lon, lat)),
                                                cell.HorizonAngleToEast,
                                                100,
                                                50,
                                                10);

                                            Fpd_s_join_n_over0innerdoor fpdItem = new Fpd_s_join_n_over0innerdoor();
                                            fpdItem.p_day = p_day;
                                            fpdItem.p_city = city;
                                            fpdItem.gridid = grid;
                                            fpdItem.longitude = lon.ToString();
                                            fpdItem.latitude = lat.ToString();
                                            fpdItem.gridx = ((int)mx / 5).ToString();
                                            fpdItem.gridy = ((int)my / 5).ToString();
                                            fpdItem.height = (floorNum * 5).ToString();
                                            fpdItem.objectid = cell.ObjectID.ToString();
                                            fpdItem.rsrp = rsrp;
                                            fpdItem.calibrategridid = calibrateGridId.ToString();

                                            lsFPD.Add(fpdItem);
                                        }
                                    }
                                }
                                line = sr.ReadLine();
                            } while (!string.IsNullOrEmpty(line));
                        }

                        #endregion
                    }
                }
                //Console.WriteLine("lsFPD.Count:" + lsFPD.Count);
                #region 9、根据栅格ID分组 10、按RSRP排序，取前11条 11、最大值为主服务小区 12、过滤主服务小区小于-120的分组 12、写文件

                foreach (var g in lsFPD.GroupBy(x => x.gridid))
                {
                    if (g.Count() > 1)
                    {
                        List<Fpd_s_join_n_over0innerdoor> groupFpd = g.ToList().OrderByDescending(o => Convert.ToDouble(o.rsrp)).ToList();

                        Fpd_s_join_n_over0innerdoor serverFpd = groupFpd[0];
                        double rsrp = Convert.ToDouble(serverFpd.rsrp);

                        if (rsrp > -120)
                        {
                            int endi = 11;
                            if (groupFpd.Count < 11) { endi = groupFpd.Count; }

                            for (int i = 1; i < endi; i++)
                            {
                                if (fileInfoNum == fileInfoCount || fileInfoNum == 0)
                                {
                                    string fileName = DateTime.Now.ToString("yyyyMMddHHmmssfff") + new Random().Next(10000);
                                    fpdFilePath = fpdPath + "\\" + fileName + ".csv";

                                    if (fs != null) fs.Flush();
                                    if (sw != null) sw.Flush();
                                    if (sw != null) sw.Dispose();
                                    if (fs != null) fs.Dispose();

                                    fs = new System.IO.FileStream(fpdFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
                                    sw = new StreamWriter(fs);

                                    fileInfoNum = 0;
                                }

                                sw.WriteLine(string.Concat(serverFpd.gridid, ",", serverFpd.longitude, ",", serverFpd.latitude, ",", serverFpd.gridx, ",", serverFpd.gridy, ",", serverFpd.height, ",", serverFpd.objectid, ",", serverFpd.rsrp, ",", serverFpd.calibrategridid, ",", groupFpd[i].objectid, ",", groupFpd[i].rsrp, ",", groupFpd[i].calibrategridid, ",", serverFpd.p_city, ",", serverFpd.p_day));

                                fileInfoNum++;
                            }
                        }
                        for (int a = groupFpd.Count - 1; a >= 0; a--)
                        {
                            groupFpd[a] = null;
                        }

                        groupFpd.Clear();
                        groupFpd.Capacity = 0;
                        groupFpd = null;
                    }
                }

                for (int a = lsFPD.Count - 1; a >= 0; a--)
                {
                    lsFPD[a] = null;
                }
                lsFPD.Clear();
                lsFPD.Capacity = 0;
                lsFPD = null;

                #endregion
            }

            for (int a = lsCell.Count - 1; a >= 0; a--)
            {
                lsCell[a] = null;
            }

            lsCell.Clear();
            lsCell.Capacity = 0;
            lsCell = null;

            dicLsCell.Clear();
            GC.Collect();

            if (fs != null) fs.Flush();
            if (sw != null) sw.Flush();
            if (sw != null) sw.Dispose();
            if (fs != null) fs.Dispose();
        }


        /// <summary>
        /// 计算 当前栅格的校准栅格ID
        /// </summary>
        /// <param name="cellPoint">小区点</param>
        /// <param name="gridPoint">栅格点</param>
        /// <param name="cellHorizonAngleToEast">小区水平方向角</param>
        /// <param name="angleNum">角度的栅格个数</param>
        /// <param name="distainceGridNum">距离的栅格个数</param>
        /// <param name="deltD">距离的步长</param>
        /// <returns>校准后的栅格ID</returns>
        public static int ComputeCalibrateGridID(DTGroup.iNek.Utility.GeometryPoint cellPoint, DTGroup.iNek.Utility.GeometryPoint gridPoint, int cellHorizonAngleToEast, int angleNum, int distainceGridNum, int deltD)
        {
            // b) 计算 栅格坐标 与小区方向的夹角  

            /* 计算栅格坐标和小区方向的夹角 
             *   1、弧度 = arctan(y1-y1/x1-x1) )                           值在-180—180之间
             *   2、angleHoritonToEast=[30-(angleHoritonToEast-90)]%360
             *   3、角度1 = 角度0-angleHoritonToEast                        值在 -540—180之间
             *   4、角度2=（角度1+720）%360
            */

            // 1、弧度 = arctan(y1-y1/x1-x1) )    值在-180—180之间
            double radian = Math.Atan2((gridPoint.STY - cellPoint.STY), (gridPoint.STX - cellPoint.STX));
            // 将 弧度转换为角度 弧度转角度= 180/PI*弧度
            int angle0 = Convert.ToInt32(180 / Math.PI * radian);
            // 2、angleHoritonToEast=[30-(angleHoritonToEast-90)]%360
            int angleHoritonToEast = (360 - (cellHorizonAngleToEast - 90)) % 360;
            // 3、角度1 = 角度0-angleHoritonToEast 
            int angle1 = angle0 - angleHoritonToEast;
            // 4、角度2=（角度1+720）%360
            int angle2 = (angle1 + 720) % 360;

            // c) 计算夹角的索引

            /*
             * 1、角度3=（角度2+180/a）%360;                             0-360
             * 2、tempA = Math.Cell(角度3*a/360)                         0-19
             */

            // 1、角度3=（角度2+180/a）%360;  
            int angle3 = (angle2 + 180 / angleNum) % 360;
            // 2、计算角度索引 tempA = Math.Cell(角度3*a/360)     
            int angleIndex = Convert.ToInt32(Math.Floor(angle3 * angleNum / 360d));

            // 小区和栅格的中心点的距离
            double d = DTGroup.iNek.Utility.GeometryUtil.GetDistanceOfTwoPoints(gridPoint, cellPoint);
            // d) 计算距离索引
            // b = min(Math.Celling())
            double tempB = Math.Min(Math.Floor(d / deltD), distainceGridNum - 1);

            // e) 计算校准的栅格编号
            return Convert.ToInt32(angleIndex * distainceGridNum + tempB);
        }

        private static string GetName(string str)
        {
            if (string.IsNullOrEmpty(str)) return "";

            if (str.Length > 0)
            {
                if (str.LastIndexOf("/") > 0)
                {
                    str = str.Substring(str.LastIndexOf("/") + 1);
                }
                else if (str.LastIndexOf("\\") > 0)
                {
                    str = str.Substring(str.LastIndexOf("\\") + 1);
                }
            }
            return str;
        }

        public static void loc_to_mercator(double lon, double lat, ref double miX, ref double minY)
        {
            double x = lon;
            double y = Math.Log(Math.Tan((lat / 180 * Math.PI + Math.PI / 2) / 2)) * 180 / Math.PI;
            miX = x * 20037508.3427892 / 180;
            minY = y * 20037508.3427892 / 180;
        }

        public static void mercator_to_loc(double mercator_x, double mercator_y, ref double lon, ref double lat)
        {
            double x = mercator_x / 20037508.3427892 * 180;
            double y = mercator_y / 20037508.3427892 * 180;
            lon = x;
            lat = 180 / Math.PI * (2 * Math.Atan(Math.Exp(y * Math.PI / 180)) - Math.PI / 2);
        }
    }


    public class SyntheticPar
    {
        public string City { get; set; }
        public string CellPath { get; set; }
        public string WinpropOutPut { get; set; }
        public string FPDPath { get; set; }
    }
    public class Fpd_s_join_n_over0innerdoor
    {
        public string p_day { get; set; }
        public string p_city { get; set; }
        public string gridid { get; set; }
        public string longitude { get; set; }
        public string latitude { get; set; }
        public string gridx { get; set; }
        public string gridy { get; set; }
        public string height { get; set; }
        public string objectid { get; set; }
        public string rsrp { get; set; }
        public string calibrategridid { get; set; }
        public string n_objectid { get; set; }
        public string n_rsrp { get; set; }
        public string ncalibrategridid { get; set; }
    }

    public class Interval
    {
        public double minX;
        public double minY;
    }
}
